package com.cyz.manager.upload;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.cyz.config.CosClientConfig;
import com.cyz.exception.BusinessException;
import com.cyz.exception.ErrorCode;
import com.cyz.manager.CosManager;
import com.cyz.model.dto.file.UploadPictureResult;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.model.COSObjectInputStream;
import com.qcloud.cos.model.GetObjectRequest;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.CIObject;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.qcloud.cos.model.ciModel.persistence.ProcessResults;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;


@Slf4j
@Service
public abstract class PictureUploadTemplate {

    @Resource
    private CosClientConfig cosClientConfig;

    @Resource
    private CosManager cosManager;

    @Resource
    private COSClient cosClient;

    /**
     * 上传并解析图片
     *
     * @param inputSource      输入源
     * @param uploadPathPrefix 上传图片的路径前缀
     * @return 解析完成的信息
     */
    public UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix) {
        // 1. 校验图片
        validFile(inputSource);

        // 获取文件原始名称，用于存储到数据库中
        String originalFilename = getOriginalFilename(inputSource);

        // 如果是url链接判断文件后缀是否常规
        if (inputSource instanceof String) {
            String fileUrl = (String) inputSource;
            // 获取图片后缀
            String fileSuffix = FileUtil.getSuffix(fileUrl);
            log.info("文件后缀=========================================>{}", fileSuffix);
            if (fileSuffix.length() > 5) {
                // 将特殊格式文件转换成png格式
                fileUrl = FileUtil.getPrefix(fileUrl) + ".png";

                // 将转换格式后的文件赋值
                originalFilename = fileUrl;
            }
        }

        // 2. 拼接图片地址
        String uuid = RandomUtil.randomString(16);
        // 获取文件名称
        String uploadFileName = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid, FileUtil.getSuffix(originalFilename));
        String uploadPath = String.format("/%s/%s", uploadPathPrefix, uploadFileName);

        // 3. 图片上传
        File file = null;
        try {
            // 4. 处理文件源，并创建临时文件
            file = File.createTempFile(uploadPath, null);
            processFile(inputSource, file);

            // 5. 上传到cos对象存储服务器中
            PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);

            // 6. 获取图片信息
            ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            // 获取处理后的文件信息
            ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults();
            List<CIObject> objectList = processResults.getObjectList();
            if (CollUtil.isNotEmpty(objectList)) {
                // 获取压缩后的文件信息
                CIObject compressedCiObject = objectList.get(0);
                // 缩略图默认为压缩图
                CIObject thumbnailObject = compressedCiObject;
                // 获取缩略图的文件信息
                if (objectList.size() > 1) {
                    thumbnailObject = objectList.get(1);
                }
                // 封装返回结果
                return getUploadPictureResult(originalFilename, compressedCiObject, thumbnailObject, uploadPath);
            }
            // 返回原文件封装信息
            return getUploadPictureResult(originalFilename, uploadPath, file, imageInfo);
        } catch (IOException e) {
            log.info("图片上传失败,filePath = {}", uploadPath, e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
        } finally {
            // 7. 清理临时文件
            clearTempFile(file);
        }
    }


    /**
     * 封装返回结果
     *
     * @param originalFilename   原文件名称
     * @param compressedCiObject 数据万象处理后的图片信息
     * @param thumbnailObject    缩略图信息
     */
    private UploadPictureResult getUploadPictureResult(
            String originalFilename, CIObject compressedCiObject,
            CIObject thumbnailObject, String uploadPath) {

        String format = compressedCiObject.getFormat();
        int width = compressedCiObject.getWidth();
        int height = compressedCiObject.getHeight();
        double picScale = NumberUtil.round(width * 1.0 / height, 2).doubleValue();

        // 获取图片主色调
        String imageAve = getImageAve(uploadPath);

        // 封装图片信息
        UploadPictureResult uploadPictureResult = new UploadPictureResult();
        uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + compressedCiObject.getKey());  // 压缩后的图片地址
        uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailObject.getKey());    // 缩略图地址
        uploadPictureResult.setOriginalUrl(cosClientConfig.getHost() + "/" + uploadPath);     //原图地址
        uploadPictureResult.setName(FileUtil.mainName(originalFilename));
        uploadPictureResult.setPicSize(compressedCiObject.getSize().longValue());
        uploadPictureResult.setPicWidth(width);
        uploadPictureResult.setPicHeight(height);
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(format);
        uploadPictureResult.setPicColor(imageAve);
        // 返回图片信息
        return uploadPictureResult;
    }

    /**
     * 封装返回结果
     *
     * @param originalFilename 源文件名
     * @param uploadPath       上传的文件地址
     * @param file             临时存储到本地的图片
     * @param imageInfo        图片信息
     * @return 封装完成的文件信息
     */
    private UploadPictureResult getUploadPictureResult(String originalFilename, String uploadPath, File file, ImageInfo imageInfo) {
        String format = imageInfo.getFormat();
        int width = imageInfo.getWidth();
        int height = imageInfo.getHeight();
        double picScale = NumberUtil.round(width * 1.0 / height, 2).doubleValue();

        // 获取图片主色调
        String imageAve = getImageAve(uploadPath);

        // 封装图片信息
        UploadPictureResult uploadPictureResult = new UploadPictureResult();
        uploadPictureResult.setUrl(cosClientConfig.getHost() + uploadPath);
        uploadPictureResult.setName(FileUtil.mainName(originalFilename));
        uploadPictureResult.setPicSize(FileUtil.size(file));
        uploadPictureResult.setPicWidth(width);
        uploadPictureResult.setPicHeight(height);
        uploadPictureResult.setPicScale(picScale);
        uploadPictureResult.setPicFormat(format);
        uploadPictureResult.setPicColor(imageAve);
        // 返回图片信息
        return uploadPictureResult;
    }

    /**
     * 清理临时文件
     *
     * @param file 临时文件
     */
    private static void clearTempFile(File file) {
        if (file != null) {
            boolean deleteResult = file.delete();
            if (!deleteResult) {
                log.info("临时文件删除失败,filePath = {}", file.getAbsolutePath());
            }
        }
    }


    /**
     * 获取图片主色调
     *
     * @param key 文件 key
     * @return 图片主色调
     */
    public String getImageAve(String key) {

        log.info("获取图片：{}，的主色调", key);

        GetObjectRequest getObj = new GetObjectRequest(cosClientConfig.getBucket(), key);
        String rule = "imageAve";
        getObj.putCustomQueryParameter(rule, null);
        COSObject object = cosClient.getObject(getObj);
        COSObjectInputStream objectContent = object.getObjectContent();
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            CloseableHttpResponse httpResponse = httpClient.execute(objectContent.getHttpRequest());
            String response = EntityUtils.toString(httpResponse.getEntity());
            return JSONUtil.parseObj(response).getStr("RGB");
        } catch (IOException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 校验输入源
     *
     * @param inputSource 输入源(本地文件或 URL)
     */
    protected abstract void validFile(Object inputSource);

    /**
     * 获取输入源的原始文件名
     *
     * @param inputSource 输入源(本地文件或 URL)
     */
    protected abstract String getOriginalFilename(Object inputSource);

    /**
     * 处理输入源并生成本地临时文件
     *
     * @param inputSource 输入源(本地文件或 URL)
     */
    protected abstract void processFile(Object inputSource, File file) throws IOException;


}
